package com.googlecode.androidkurskoblenz.jledcube;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.TextEvent;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Observable;

import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingBox;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.VirtualUniverse;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.image.TextureLoader;

/**
 * LedCube
 *
 * Creates a cube
 */
public abstract class LedCube extends Observable
{
	public static final float CAMERA_Z = 20;
	public static final List<Color> colors = Arrays.asList(
		Color.GREEN, Color.WHITE, Color.BLUE, Color.RED, Color.YELLOW);
	
	private LedLayer[] layers;
	private int selectedColor;
	private float size;
	private VirtualUniverse universe;
	private int width, depth, height;
	
	public LedCube()
	{
                // 20 = y, 
		this(5, 5, 10, 0.1f);
	}
	
	public LedCube(int width, int depth, int height, float size)
	{
		this.width = width; // y
		this.depth = depth; // x
		this.height = height; // z
		this.size = size;
		this.selectedColor = 0;
		
		this.layers = new LedLayer[height];
		this.universe = createUniverse();
		
		BranchGroup bg = new BranchGroup();
		
		// neue Transform Informationen
		Transform3D transform3d = new Transform3D();
		//transform3d.rotX(Math.toRadians(180));
                //transform3d.rotZ(Math.toRadians(90));
                //transform3d.rotY(Math.toRadians(90));
                //transform3d.set(new Vector3f(-1.5f, 1.5f, .0f));
                transform3d.setTranslation(new Vector3f(0.0f,0.0f,0.0f));
		// neue Transformgruppe
		TransformGroup ledGroup = new TransformGroup(transform3d);
		ledGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		ledGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		TransformGroup tg = new TransformGroup();
		tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tg.addChild(ledGroup);
                // Test
                //new Led(0, 0, 0, 0.5f, tg, this.getColor());
		bg.addChild(tg);
		BoundingBox boundingBox = new BoundingBox(new Point3d(0, 0, 0), new Point3d(depth, height, width));
		
		// Add background image
		Sphere backgroundSphere = new Sphere(
			15.0f,
			Sphere.GENERATE_NORMALS | Sphere.GENERATE_NORMALS_INWARD | Sphere.GENERATE_TEXTURE_COORDS,
			100
		);
		
		URL bgImage = getClass().getClassLoader().getResource("res/Background.png");
		TextureLoader tex = new TextureLoader(bgImage, new String("RGB"), this.getCanvas());
		Appearance backgroundApp = backgroundSphere.getAppearance();
		if(tex != null)
		{
			backgroundApp.setTexture(tex.getTexture());
		}
		tg.addChild(backgroundSphere);
		
		for(int z = 0; z < height; z++)
		{
			LedLayer layer = new LedLayer(z, depth, width, size, ledGroup, this, this.getColor());
			layer.setPos(z);
			this.layers[z] = layer;
		}
		
		// Rotate with left mouse
		MouseRotate mouseRotate = new MouseRotate();
		mouseRotate.setTransformGroup(tg);
		mouseRotate.setSchedulingBounds(boundingBox);
		bg.addChild(mouseRotate);
		
		// Moving with right click
		MouseTranslate mouseTranslate = new MouseTranslate();
		mouseTranslate.setTransformGroup(tg);
		mouseTranslate.setSchedulingBounds(boundingBox);
		bg.addChild(mouseTranslate);
		
		// Zoom with middle click
		MouseZoom mouseZoom = new MouseZoom();
		mouseZoom.setTransformGroup(tg);
		mouseZoom.setSchedulingBounds(boundingBox);
		bg.addChild(mouseZoom);
		
		// Key navigation (moving)
		KeyNavigatorBehavior keyNavigatorBehavior = new KeyNavigatorBehavior(tg);
		keyNavigatorBehavior.setSchedulingBounds(boundingBox);
		bg.addChild(keyNavigatorBehavior);
		
		bg.compile();
		
		this.addBranchGroup(bg);
	}
	
	protected abstract void addBranchGroup(BranchGroup bg);
	
	protected abstract VirtualUniverse createUniverse();
	
	protected abstract Component getCanvas();
	
	public void changeColor()
	{
                // If the color of led changes, we inform the leds
		this.selectedColor++;
		if(this.selectedColor > LedCube.colors.size())
		{
			this.selectedColor = 0;
		}
		
		this.setChanged();
		this.notifyObservers(new ActionEvent(this.getColor(), ActionEvent.ACTION_FIRST, "color"));
	}
	
	public void changeEnable(int layer, int row, int col, boolean enabled)
	{
                // If the state of led changes, we inform the leds
		// layer: z
		// row : y
		// col : x
		int[] params = new int[4];
		params[0] = (enabled ? 1 : 0);
		params[1] = col;
		params[2] = row;
		params[3] = layer;
		
		this.setChanged();
		this.notifyObservers(new TextEvent(params, ActionEvent.ACTION_FIRST));
	}
	
	public Color getColor()
	{
		return LedCube.colors.get(this.selectedColor);
	}
	
	public Color getDarkerColor()
	{
		return this.getColor().darker().darker().darker();
	}
	
	public int getDepth()
	{
		return this.depth;
	}
	
	public int getHeight()
	{
		return this.height;
	}
	
	public float getSize()
	{
		return this.size;
	}
	
	public VirtualUniverse getUniverse()
	{
		return universe;
	}
	
	public int getWidth()
	{
		return this.width;
	}
}
